Add runtime egress enforcement and audit logging#9
Merged
Conversation
- Add EgressEnforcer (http.RoundTripper) that validates outbound requests against domain allowlist with wildcard support and localhost bypass - Add structured NDJSON audit logger with correlation IDs and task IDs for end-to-end request tracing - Extend HookContext with TaskID/CorrelationID, populate in all Fire() calls - Wire egress transport into all HTTP-making tools (http_request, mcp_call, webhook_call, web_search_tavily, web_search_perplexity) - Resolve egress config in runner, inject enforcer and audit logger into handler context, register audit hooks for tool/LLM/egress events - Rewrite README with full coverage of fallback chains, OAuth, memory, runtime security, guardrails, context budgeting, and config reference
naveen-kurra
pushed a commit
to naveen-kurra/forge
that referenced
this pull request
May 23, 2026
…nitializ#9) Reviewer flagged: handleCreateAgent decoded JSON, checked only name and model_provider, then handed opts.Auth (with an arbitrary Settings map[string]any) straight to CreateFunc. The validate package's ValidateAuthConfig existed but was never called on this path — so a buggy frontend could POST an oidc payload missing issuer/audience, the agent directory would scaffold cleanly with a malformed auth: block, and the error would only surface when the user ran `forge run`. By then the directory, .env.example, and channel tokens are all on disk. Fix: - New validateAuthPayload(opts.Auth) helper in handlers_create.go. - Called from handleCreateAgent immediately after the name/provider checks, BEFORE CreateFunc runs. - Translates AuthCreateOptions → types.AuthConfig exactly the way cmd/ui.go's createFunc does, so this check sees the same shape the eventual scaffold will. No drift between "wizard accepts" and "forge validate accepts". - 400 with the validation errors joined as the response message. - Modes "" / "none" / "custom" skip validation — they don't write provider entries, nothing to validate. Validation surface (delegated to validate.ValidateAuthConfig): - oidc: issuer + audience required - http_verifier: url required - static_token: token or token_env required - unknown type: rejected with list of known types The frontend's pre-submit checks (canNext in app.js step 8) already guard the happy path, but those are defense in depth at best — anyone with curl can bypass them. This is the server-side enforcement that matters. Tests: TestHandleCreateAgent_OIDCMissingIssuerRejected 400, error names "issuer" TestHandleCreateAgent_OIDCMissingAudienceRejected 400, error names "audience" TestHandleCreateAgent_OIDCBothFieldsAccepted 201 (regression) TestHandleCreateAgent_HTTPVerifierMissingURLRejected 400, error names "url" TestHandleCreateAgent_UnknownAuthModeRejected 400, error names unknown type TestHandleCreateAgent_NoneAndCustomSkipValidation 201 for mode=none and mode=custom TestHandleCreateAgent_WithoutAuthPayload 201 with nil Auth (regression) Each failure test also asserts that CreateFunc was NOT called — the agent directory must never exist on disk for malformed auth. Verification: go test -race ./forge-ui/ — green full sweep — all packages pass golangci-lint v2.10.1 — 0 issues
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
EgressEnforcer— anhttp.RoundTripperthat validates every outbound HTTP request against the resolved domain allowlist before forwarding. Supports exact match, wildcard domains (*.github.com), and always-allowed localhost. Three modes:deny-all,allowlist,dev-open.session_start,session_end,tool_exec,egress_allowed,egress_blocked,llm_call) and correlation IDs for end-to-end request tracing.TaskIDandCorrelationIDpropagated through context andHookContext, attached to all audit events including egress callbacks.http_request,mcp_call,webhook_call,web_search_tavily,web_search_perplexity) now use the egress-enforced transport/client from context.Files changed
forge-core/security/egress_enforcer.gohttp.RoundTripperwrapper + context helpersforge-core/security/egress_enforcer_test.goforge-core/security/egress_integration_test.goforge-core/runtime/audit.goforge-core/runtime/audit_test.goforge-core/runtime/audit_integration_test.goforge-core/runtime/hooks.goforge-core/runtime/loop.goforge-core/tools/builtins/http_request.goforge-core/tools/builtins/web_search_tavily.goforge-core/tools/builtins/web_search_perplexity.goforge-core/tools/adapters/mcp_call.goforge-core/tools/adapters/webhook_call.goforge-cli/runtime/runner.goREADME.mdTest plan
forge-core/security— all egress enforcer tests pass (allowlist, deny-all, dev-open, wildcards, localhost, callbacks, context helpers, integration)forge-core/runtime— all audit logger tests pass (emit, concurrent safety, context round-trips, event sequence)forge-core/tools— existing tool tests pass unchanged (backward compatible when no egress client in context)forge-cli/runtime— runner tests pass with updatedregisterHandlerssignaturegolangci-lintclean on bothforge-coreandforge-cli